﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows;

namespace 主控程序
{
    /// <summary>
    /// 为程序链接到一个已存在的动态链接库（DLL），简而言之即加载一个DLL。
    /// 需要注意的是，动态链接库无法手动回收资源，由GC统一管理，且性能受到影响
    /// </summary>
    public class 动态链接库
    {
        /// <summary>
        /// DLL的反射
        /// </summary>
        public Assembly 反射 { get; set; }
        /// <summary>
        /// 动态链接库的类
        /// </summary>
        public Dictionary<string, Type?> 动态链接类组 { get; set; } = new Dictionary<string, Type?>();
        /// <summary>
        /// 管理动态链接的实例化类组
        /// </summary>
        public Dictionary<string, object?> 类实例组 { get; set; } = new Dictionary<string, object?>();
        /// <summary>
        /// 获取或设置动态链接库的名称
        /// </summary>
        public string 动态链接库名 { get; set; }
        /// <summary>
        /// 使用反射，为程序链接一个动态链接库（DLL文件）
        /// </summary>
        /// <param name="文件路径">动态链接库（DLL）的文件路径</param>
        public 动态链接库(string 文件路径)
        {
            反射 = Assembly.LoadFile(System.IO.Path.Combine(Directory.GetCurrentDirectory(), 文件路径)); //加载dll文件
            动态链接库名 = 反射.GetName().Name!;
        }
        /// <summary>
        /// 实例化动态链接库（DLL）中的一个类
        /// </summary>
        /// <param name="类名路径">类名路径，要写明命名空间到类的路径（如：命名空间.类名）</param>
        /// <exception cref="Exception">抛出异常，类名路径不存在，当抛出此异常时，提示类名路径填写错误</exception>
        public void 实例化类(string 类名路径)
        {
            Type 动态链接类 = 反射.GetType(类名路径)!; //获取一个类，使用类的完全限定名称
            if (动态链接类 == null) { throw new Exception("错误 0002 - 类名路径不存在"); }
            动态链接类组.Add(类名路径, 动态链接类);
        }
        /// <summary>
        /// 调用已经实例化类的方法，如果类未被实例化，将自动实例化此类
        /// </summary>
        /// <param name="类名路径">类名路径，要写明命名空间到类的路径（如：命名空间.类名）</param>
        /// <param name="方法名">方法名称，此处只填写方法名称即可，不需要完整的路径</param>
        /// <exception cref="Exception">抛出异常，该反射未正常被处理，当抛出此异常时，说明反射出现问题</exception>
        public void 调用方法(string 类名路径, string 方法名)
        {
            if (反射 == null) { throw new Exception("错误 0001 - 该反射未正常被处理"); }
            Type 动态链接类 = 反射.GetType(类名路径)!; //获取一个类，使用类的完全限定名称
            if (!动态链接类组.ContainsKey(类名路径))
            {
                动态链接类组.Add(类名路径, 动态链接类);
            }
            if (!类实例组.ContainsKey(类名路径))
            {
                Object 类实例 = Activator.CreateInstance(动态链接类)!; //为类创建一个实例，相当于New()
                类实例组.Add(类名路径, 类实例);
            }
            MethodInfo 方法 = 动态链接类.GetMethod(方法名)!; //获取方法
            方法.Invoke(类实例组[类名路径], null); //在主线程上调用这个方法
        }

        /// <summary>
        /// 获取已实例类中的值
        /// </summary>
        /// <typeparam name="T">填写这个值的类型</typeparam>
        /// <param name="类名路径">类名路径，要写明命名空间到类的路径（如：命名空间.类名）</param>
        /// <param name="属性名">属性名称，此处只填写属性名称即可，不需要完整的路径</param>
        /// <returns>返回要获取的值</returns>
        /// <exception cref="Exception">抛出异常，当抛出此异常时，说明反射出现问题或属性名不存在</exception>
        public T? 获取值<T>(string 类名路径, string 属性名)
        {
            if (反射 == null) { throw new Exception("错误 0001 - 该反射未正常被处理"); }
            Type 动态链接类 = 反射.GetType(类名路径)!; //获取一个类，使用类的完全限定名称
            if (!动态链接类组.ContainsKey(类名路径))
            {
                动态链接类组.Add(类名路径, 动态链接类);
            }
            if (!类实例组.ContainsKey(类名路径))
            {
                Object 类实例 = Activator.CreateInstance(动态链接类)!; //为类创建一个实例，相当于New()
                类实例组.Add(类名路径, 类实例);
            }
            var 值 = 动态链接类.GetProperty(属性名)!; //属性名
            if (值 == null) { throw new Exception("错误 0003 - 属性名不存在"); }

            return (T)值.GetValue(类实例组[类名路径])!;
        }
        /// <summary>
        /// 设置已实例类中的属性值
        /// </summary>
        /// <typeparam name="T">填写这个值的类型</typeparam>
        /// <param name="类名路径">类名路径，要写明命名空间到类的路径（如：命名空间.类名）</param>
        /// <param name="属性名">属性名称，此处只填写属性名称即可，不需要完整的路径</param>
        /// <param name="设定值">要设定的值</param>
        /// <exception cref="Exception"></exception>
        public void 设置值<T>(string 类名路径, string 属性名, T 设定值)
        {
            if (反射 == null) { throw new Exception("错误 0001 - 该反射未正常被处理"); }
            Type 动态链接类 = 反射.GetType(类名路径)!; //获取一个类，使用类的完全限定名称
            if (!动态链接类组.ContainsKey(类名路径))
            {
                动态链接类组.Add(类名路径, 动态链接类);
            }
            if (!类实例组.ContainsKey(类名路径))
            {
                Object 类实例 = Activator.CreateInstance(动态链接类)!; //为类创建一个实例，相当于New()
                类实例组.Add(类名路径, 类实例);
            }
            var 值 = 动态链接类.GetProperty(属性名)!; //属性名
            if (值 == null) { throw new Exception("错误 0003 - 属性名不存在"); }
            值.SetValue(类实例组[类名路径], 设定值);
            return;
        }

        /// <summary>
        /// 遍历一个文件夹中的动态链接库（DLL）
        /// </summary>
        /// <param name="文件夹路径">文件夹路径</param>
        /// <param name="DLL屏蔽列表">要屏蔽的DLL列表，被屏蔽的DLL将不被加载调用</param>
        /// <returns></returns>
        public static List<string> 遍历动态链接库(string 文件夹路径, List<string> DLL屏蔽列表 = null)
        {
            var 本程序集名称 = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name!;
            var 文件列表 = Directory.GetFiles(文件夹路径, "*.dll");
            List<string> 库列表 = new();
            foreach (var 动态链接库 in 文件列表)
            {
                if (System.IO.Path.GetFileNameWithoutExtension(动态链接库).Contains(本程序集名称))
                {
                    continue;
                }
                var 是屏蔽项 = DLL屏蔽列表.TrueForAll((x) =>
                {
                    if (System.IO.Path.GetFileNameWithoutExtension(动态链接库).Contains(x))
                    {
                        return true ;
                    }
                    return false;
                });
                if (是屏蔽项) { continue; }
                库列表.Add(System.IO.Path.GetFileName(动态链接库));
            }


            return 库列表;
        }

    }


    public partial class MainWindow : Window
    {
        public Dictionary<string, 动态链接库> 动态链接库集 = new();
        public MainWindow()
        {
            InitializeComponent();
            var 动态链接库列表 = 动态链接库.遍历动态链接库(Directory.GetCurrentDirectory());
            批量调用方法(动态链接库列表, "组件.初始化", "初始化组件");
            var 值 = 动态链接库集["组件1"].获取值<bool>("组件.初始化", "主窗体启动");
            Debug.WriteLine(值);

        }



        public void 批量调用方法(List<string> Dll文件组, string 类名路径, string 方法名)
        {
            foreach (var Dll文件名 in Dll文件组)
            {
                动态链接库 动态链接库 = new(Dll文件名);
                动态链接库集.Add(动态链接库.动态链接库名, 动态链接库);
                动态链接库.实例化类(类名路径);
                动态链接库.调用方法(类名路径, 方法名);
            }

        }



    }
}
